#include <common.h>
#include <ddr.h>

/* #define DEBUG_READ_WRITE */

#define ddr_writel(value, reg)  writel((value), DDRC_BASE + reg)
#define ddr_readl(reg)      readl(DDRC_BASE + reg)

#define CONFIG_DWC_DEBUG 0
#define ddr_hang() do {						\
		serial_puts(__FUNCTION__); serial_puts("  line = 0x"); serial_puthex(__LINE__);  \
	} while(0)

#ifdef  CONFIG_DWC_DEBUG
#define FUNC_ENTER() serial_puts(__FUNCTION__); serial_puts("enter.\n");
#define FUNC_EXIT() serial_puts(__FUNCTION__); serial_puts("exit.\n");

static void dump_ddrc_register(void)
{
	serial_puts("DDRC_STATUS         0x"); serial_puthex(ddr_readl(DDRC_STATUS));
	serial_puts("DDRC_CFG            0x"); serial_puthex(ddr_readl(DDRC_CFG));
	serial_puts("DDRC_CTRL           0x"); serial_puthex(ddr_readl(DDRC_CTRL));
	serial_puts("DDRC_LMR            0x"); serial_puthex(ddr_readl(DDRC_LMR));
	serial_puts("DDRC_DLP            0x"); serial_puthex(ddr_readl(DDRC_DLP));
	serial_puts("DDRC_TIMING1        0x"); serial_puthex(ddr_readl(DDRC_TIMING(1)));
	serial_puts("DDRC_TIMING2        0x"); serial_puthex(ddr_readl(DDRC_TIMING(2)));
	serial_puts("DDRC_TIMING3        0x"); serial_puthex(ddr_readl(DDRC_TIMING(3)));
	serial_puts("DDRC_TIMING4        0x"); serial_puthex(ddr_readl(DDRC_TIMING(4)));
	serial_puts("DDRC_TIMING5        0x"); serial_puthex(ddr_readl(DDRC_TIMING(5)));
	serial_puts("DDRC_REFCNT         0x"); serial_puthex(ddr_readl(DDRC_REFCNT));
	serial_puts("DDRC_AUTOSR_CNT     0x"); serial_puthex(ddr_readl(DDRC_AUTOSR_CNT));
	serial_puts("DDRC_AUTOSR_EN      0x"); serial_puthex(ddr_readl(DDRC_AUTOSR_EN));
	serial_puts("DDRC_MMAP0          0x"); serial_puthex(ddr_readl(DDRC_MMAP0));

	serial_puts("DDRC_MMAP1          0x"); serial_puthex(ddr_readl(DDRC_MMAP1));
	serial_puts("DDRC_REMAP1         0x"); serial_puthex(ddr_readl(DDRC_REMAP(1)));
	serial_puts("DDRC_REMAP2         0x"); serial_puthex(ddr_readl(DDRC_REMAP(2)));
	serial_puts("DDRC_REMAP3         0x"); serial_puthex(ddr_readl(DDRC_REMAP(3)));
	serial_puts("DDRC_REMAP4         0x"); serial_puthex(ddr_readl(DDRC_REMAP(4)));
	serial_puts("DDRC_REMAP5         0x"); serial_puthex(ddr_readl(DDRC_REMAP(5)));
	serial_puts("DDRC_DWCFG          0x"); serial_puthex(ddr_readl(DDRC_DWCFG));
	serial_puts("DDRC_HREGPRO        0x"); serial_puthex(ddr_readl(DDRC_HREGPRO));
	serial_puts("DDRC_PREGPRO        0x"); serial_puthex(ddr_readl(DDRC_PREGPRO));
	serial_puts("DDRC_CGUC0          0x"); serial_puthex(ddr_readl(DDRC_CGUC0));
	serial_puts("DDRC_CGUC1          0x"); serial_puthex(ddr_readl(DDRC_CGUC1));

	serial_puts("#define timing1_tWL         0x"); serial_puthex(timing1_tWL);
	serial_puts("#define timing1_tWR         0x"); serial_puthex(timing1_tWR);
	serial_puts("#define timing1_tWTR        0x"); serial_puthex(timing1_tWTR);
	serial_puts("#define timing1_tWDLAT      0x"); serial_puthex(timing1_tWDLAT);

	serial_puts("#define timing2_tRL         0x"); serial_puthex(timing2_tRL);
	serial_puts("#define timing2_tRTP        0x"); serial_puthex(timing2_tRTP);
	serial_puts("#define timing2_tRTW        0x"); serial_puthex(timing2_tRTW);
	serial_puts("#define timing2_tRDLAT      0x"); serial_puthex(timing2_tRDLAT);

	serial_puts("#define timing3_tRP         0x"); serial_puthex(timing3_tRP);
	serial_puts("#define timing3_tCCD        0x"); serial_puthex(timing3_tCCD);
	serial_puts("#define timing3_tRCD        0x"); serial_puthex(timing3_tRCD);
	serial_puts("#define timing3_ttEXTRW     0x"); serial_puthex(timing3_ttEXTRW);

	serial_puts("#define timing4_tRRD        0x"); serial_puthex(timing4_tRRD);
	serial_puts("#define timing4_tRAS        0x"); serial_puthex(timing4_tRAS);
	serial_puts("#define timing4_tRC         0x"); serial_puthex(timing4_tRC);
	serial_puts("#define timing4_tFAW        0x"); serial_puthex(timing4_tFAW);

	serial_puts("#define timing5_tCKE        0x"); serial_puthex(timing5_tCKE);
	serial_puts("#define timing5_tXP         0x"); serial_puthex(timing5_tXP);
	serial_puts("#define timing5_tCKSRE      0x"); serial_puthex(timing5_tCKSRE);
	serial_puts("#define timing5_tCKESR      0x"); serial_puthex(timing5_tCKESR);
	serial_puts("#define timing5_tXS         0x"); serial_puthex(timing5_tXS);
}

static void dump_ddrp_register(void)
{
	serial_puts("DDRP_INNOPHY_INNO_PHY_RST       0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_INNO_PHY_RST    ));
	serial_puts("DDRP_INNOPHY_MEM_CFG            0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_MEM_CFG         ));
	serial_puts("DDRP_INNOPHY_DQ_WIDTH           0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_DQ_WIDTH        ));
	serial_puts("DDRP_INNOPHY_CL                 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CL              ));
	serial_puts("DDRP_INNOPHY_AL                 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_AL              ));
	serial_puts("DDRP_INNOPHY_CWL                0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CWL         ));
	serial_puts("DDRP_INNOPHY_PLL_FBDIV          0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_PLL_FBDIV       ));
	serial_puts("DDRP_INNOPHY_PLL_CTRL           0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_PLL_CTRL        ));
	serial_puts("DDRP_INNOPHY_PLL_PDIV           0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_PLL_PDIV        ));
	serial_puts("DDRP_INNOPHY_PLL_LOCK           0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_PLL_LOCK        ));
	serial_puts("DDRP_INNOPHY_TRAINING_CTRL      0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_TRAINING_CTRL   ));
	serial_puts("DDRP_INNOPHY_CALIB_DONE         0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DONE      ));
	serial_puts("DDRP_INNOPHY_CALIB_DELAY_AL     0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AL  ));
	serial_puts("DDRP_INNOPHY_CALIB_DELAY_AH     0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AH  ));
	serial_puts("DDRP_INNOPHY_CALIB_BYPASS_AL    0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AL ));
	serial_puts("DDRP_INNOPHY_CALIB_BYPASS_AH    0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AH ));
	serial_puts("DDRP_INNOPHY_CALIB_DELAY_BL     0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BL  ));
	serial_puts("DDRP_INNOPHY_CALIB_DELAY_BH     0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BH  ));
	serial_puts("DDRP_INNOPHY_WL_MODE1           0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_WL_MODE1        ));
	serial_puts("DDRP_INNOPHY_WL_MODE2           0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_WL_MODE2        ));
	serial_puts("DDRP_INNOPHY_WL_DONE            0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_WL_DONE         ));
	serial_puts("DDRP_INNOPHY_INIT_COMP          0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_INIT_COMP       ));

	serial_puts("DDRP_INNOPHY_TRAINING_2c        0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_TRAINING_2c ));
	serial_puts("DDRP_INNOPHY_TRAINING_3c        0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_TRAINING_3c ));
	serial_puts("DDRP_INNOPHY_TRAINING_4c        0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_TRAINING_4c ));
	serial_puts("DDRP_INNOPHY_TRAINING_5c        0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_TRAINING_5c ));
}

#else
#define FUNC_ENTER()
#define FUNC_EXIT()

#define dump_ddrc_register()
#define dump_ddrp_register()
#endif

#if 0
static void mem_remap(void)
{
	int i;
	unsigned int remap_array[] = REMMAP_ARRAY;

	for(i = 0; i < sizeof(remap_array) / 4; i++)
		ddr_writel(remap_array[i],DDRC_REMAP(i + 1));
}
#endif

static void ddrc_post_init(void)
{
	ddr_writel(0x40C25081, DDRC_REFCNT);
	/*mem_remap();*/
	serial_puts("DDRC_STATUS: 0x"); serial_puthex(ddr_readl(DDRC_STATUS));
	ddr_writel(DDRC_CTRL_VALUE, DDRC_CTRL);

	ddr_writel(DDRC_CGUC0_VALUE, DDRC_CGUC0);
	ddr_writel(DDRC_CGUC1_VALUE, DDRC_CGUC1);

}

static void ddrc_prev_init(void)
{
	FUNC_ENTER();
	/* DDRC CFG init*/
	/* /\* DDRC CFG init*\/ */
	/* ddr_writel(DDRC_CFG_VALUE, DDRC_CFG); */
	/* DDRC timing init*/
	ddr_writel(0x040D0605, DDRC_TIMING(1));
	ddr_writel(0x03070406, DDRC_TIMING(2));
	ddr_writel(0x03060406, DDRC_TIMING(3));
	ddr_writel(0x12151004, DDRC_TIMING(4));
	ddr_writel(0x80045043, DDRC_TIMING(5));

	/* DDRC memory map configure*/
	ddr_writel(0, DDRC_MMAP0);
	ddr_writel(0x4000, DDRC_MMAP1);

	/* ddr_writel(DDRC_CTRL_CKE, DDRC_CTRL); */
	ddr_writel(DDRC_CTRL_VALUE & ~(7 << 12), DDRC_CTRL);
	FUNC_EXIT();
}

static enum ddr_type get_ddr_type(void)
{
	int type;

	ddrc_cfg_t ddrc_cfg;
	ddrc_cfg.d32 = DDRC_CFG_VALUE;
	switch(ddrc_cfg.b.TYPE){
	case 3:
		type = LPDDR;
		break;
	case 4:
		type = DDR2;
		break;
	case 5:
		type = LPDDR2;
		break;
	case 6:
		type = DDR3;
		break;
	default:
		type = UNKOWN;
		serial_puts("unsupport ddr type!\n");
		ddr_hang();
	}

	return type;
}

static void ddrc_reset_phy(void)
{
	FUNC_ENTER();
	ddr_writel(0xf << 20, DDRC_CTRL);
	mdelay(1);
	ddr_writel(0x8 << 20, DDRC_CTRL);  //dfi_reset_n low for innophy
	mdelay(1);
	FUNC_EXIT();
}

static struct jzsoc_ddr_hook *ddr_hook;
void register_ddr_hook(struct jzsoc_ddr_hook * hook)
{
	ddr_hook = hook;
}

static void ddrp_pll_init(void)
{
	unsigned int val;

	FUNC_ENTER();
	val = ddr_readl(DDRP_INNOPHY_PLL_FBDIV);
	val &= ~(0xff);
	val |= 0x14;
	ddr_writel(val, DDRP_INNOPHY_PLL_FBDIV);

	val = ddr_readl(DDRP_INNOPHY_PLL_CTRL);
	val &= ~(0xff);
	val |= 0x1a;
	ddr_writel(val, DDRP_INNOPHY_PLL_CTRL);

	val = ddr_readl(DDRP_INNOPHY_PLL_PDIV);
	val &= ~(0xff);
	val |= 0x5;
	ddr_writel(val, DDRP_INNOPHY_PLL_PDIV);

	val = ddr_readl(DDRP_INNOPHY_PLL_CTRL);
	val &= ~(0xff);
	val |= 0x18;
	ddr_writel(val, DDRP_INNOPHY_PLL_CTRL);

	val = ddr_readl(DDRP_INNOPHY_MEM_CFG);
	val &= ~(0xff);
	val |= 0x10;
	ddr_writel(val, DDRP_INNOPHY_MEM_CFG);

	val = ddr_readl(DDRP_INNOPHY_INNO_PHY_RST);
	val &= ~(0xff);
	val |= 0x0d;
	ddr_writel(val, DDRP_INNOPHY_INNO_PHY_RST);

	val = ddr_readl(DDRP_INNOPHY_CWL);
	val &= ~(0xf);
	/* val |= DDRP_CWL_VALUE; */
	val = 0x8;
	ddr_writel(val, DDRP_INNOPHY_CWL);

	val = ddr_readl(DDRP_INNOPHY_CL);
	val &= ~(0xf);
	/* val |= DDRP_CL_VALUE; */
	val |= 0x6;
	ddr_writel(val, DDRP_INNOPHY_CL);

	val = ddr_readl(DDRP_INNOPHY_AL);
	val &= ~(0xf);
	val = 0x0;
	ddr_writel(val, DDRP_INNOPHY_AL);

	while(!(ddr_readl(DDRP_INNOPHY_PLL_LOCK) & 1 << 3))
		asm("nop");
	FUNC_EXIT();
}

#if 0
static void ddrp_register_cfg(void)
{
	unsigned int val;

#ifdef DEBUG_READ_WRITE
	val = ddr_readl(DDRP_INNOPHY_DQ_WIDTH);
	val &= ~(0x3);
	val |= DDRP_DQ_WIDTH_DQ_H | DDRP_DQ_WIDTH_DQ_L;
	ddr_writel(val, DDRP_INNOPHY_DQ_WIDTH);

	val = ddr_readl(DDRP_INNOPHY_MEM_CFG);
	val &= ~(0x3 | 1 << 4);
	val |= 1 << 4 | 3;
	ddr_writel(val, DDRP_INNOPHY_MEM_CFG);

	serial_puts("ddr_readl(DDRP_INNOPHY_CL)  0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CL));
	serial_puts("ddr_readl(DDRP_INNOPHY_CWL) 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CWL));
#else
	ddr_writel(DDRP_DQ_WIDTH_DQ_H | DDRP_DQ_WIDTH_DQ_L, DDRP_INNOPHY_DQ_WIDTH);
	ddr_writel(DDRP_MEMCFG_VALUE, DDRP_INNOPHY_MEM_CFG);
#endif

	val = ddr_readl(DDRP_INNOPHY_CL);
	val &= ~(0xf);
	val |= DDRP_CL_VALUE;
	ddr_writel(val, DDRP_INNOPHY_CL);

	val = ddr_readl(DDRP_INNOPHY_CWL);
	val &= ~(0xf);
	val |= DDRP_CWL_VALUE;
	ddr_writel(val, DDRP_INNOPHY_CWL);

	val = ddr_readl(DDRP_INNOPHY_AL);
	val &= ~(0xf);
	ddr_writel(val, DDRP_INNOPHY_AL);

	serial_puts("ddr_readl(DDRP_INNOPHY_CL)  0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CL));
	serial_puts("ddr_readl(DDRP_INNOPHY_CWL) 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CWL));
	serial_puts("ddr_readl(DDRP_INNOPHY_AL)  0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_AL));
}
#endif

union ddrp_calib {
	/** raw register data */
	unsigned char d8;
	/** register bits */
	struct {
		unsigned dllsel:3;
		unsigned ophsel:1;
		unsigned cyclesel:3;
		unsigned reserved7:1;
	} calib;                    /* calib delay/bypass al/ah */
};

static void ddrp_hardware_calibration(void)
{
	unsigned int val;
	unsigned int timeout = 1000000;

	/* ddr_writel(ddr_readl(DDRP_INNOPHY_TRAINING_CTRL) | 1, DDRP_INNOPHY_TRAINING_CTRL); */
	ddr_writel(1, DDRP_INNOPHY_TRAINING_CTRL);
	do {
		val = ddr_readl(DDRP_INNOPHY_CALIB_DONE);
	} while (((val & 0xf) != 0x3) && timeout--);

	if(!timeout) {
		serial_puts("timeout:INNOPHY_CALIB_DONE 0x%"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DONE));
		ddr_hang();
	}

	ddr_writel(0, DDRP_INNOPHY_TRAINING_CTRL);

	{
		union ddrp_calib al, ah;
		al.d8 = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AL);
		serial_puts("auto :CALIB_AL:\n");
		serial_puts("dllsel 0x"); serial_puthex(al.calib.dllsel);
		serial_puts("ophsel 0x"); serial_puthex(al.calib.ophsel);
		serial_puts("cyclesel 0x"); serial_puthex(al.calib.cyclesel);

		ah.d8 = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AH);
		serial_puts("auto :CALIB_AH:\n");
		serial_puts("dllsel 0x"); serial_puthex(ah.calib.dllsel);
		serial_puts("ophsel 0x"); serial_puthex(ah.calib.ophsel);
		serial_puts("cyclesel 0x"); serial_puthex(ah.calib.cyclesel);
	}
}

/*
 * Name     : ddrp_calibration_manual()
 * Function : control the RX DQS window delay to the DQS
 *
 * a_low_8bit_delay = al8_2x * clk_2x + al8_1x * clk_1x;
 * a_high_8bit_delay    = ah8_2x * clk_2x + ah8_1x * clk_1x;
 *
 * */
static void ddrp_software_calibration(void)
{
#ifdef DEBUG_READ_WRITE
	ddr_writel(ddr_readl(DDRP_INNOPHY_TRAINING_CTRL) | DDRP_TRAINING_CTRL_DSCSE_BP, DDRP_INNOPHY_TRAINING_CTRL);
#else
	ddr_writel(DDRP_TRAINING_CTRL_DSCSE_BP, DDRP_INNOPHY_TRAINING_CTRL);
#endif

	int x, y;
	int c, o, d;
	unsigned int addr = 0x80010000, val;
	unsigned int i, n, m = 0;
	union ddrp_calib calib_val[8 * 2 * 8];

#if 1
	for(c = 0; c < 8; c ++)
		for(o = 0; o < 2; o++) {
			x = c << 4 | o << 3 ;

			for(d = 0; d < 8; d++) {
				y = d << 0;
				ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_AL);
				ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AL) | y, DDRP_INNOPHY_CALIB_BYPASS_AL);
				/* soft_bypass_al = ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AL);
				   delay_al = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AL);
				   serial_puts("### soft_bypass_al = 0x")
				   serial_puthex(soft_bypass_al);
				   serial_puts("delay_al = 0x");
				   serial_puthex(delay_al);*/
				mdelay(1);
				for(i = 0; i < 0xff; i ++) {
					val = 0;
					for(n = 0; n < 4; n++ ) {
						val |= i <<(n * 8);
						val &= 0x000000ff;
					}
					writel(val, (addr + i * 4));
					__asm__ __volatile__("csrrw x0,0x800, %0"::"r"(addr+i*4));
					__asm__ __volatile__("csrrw x0,0x5c2, %0"::"r"(addr+i*4));
					val &= 0x000000ff;
					if((readl(addr + i * 4) & 0x000000ff)!= val)
						break;
				}
				if(i == 0xff) {
					calib_val[m].calib.cyclesel = c;
					calib_val[m].calib.ophsel = o;
					/*calib_val[m].calib.dllsel = d;*/
					m++;
					serial_puts("AL ok VALUE 0x");serial_puthex(x);
				}
			}
		}

	if(!m) {
		serial_puts("AL calib bypass fail\n");
		return ;
	}
	/* for(i = 0; i <= m; i++) */
	m /= 2;
	c = calib_val[m].calib.cyclesel;
	o = calib_val[m].calib.ophsel;
	/*d = calib_val[m].calib.dllsel;*/

	/*x = c << 4 | o << 3 | d;*/
	x = 0;
	x = c << 4 | o << 3 | 0;
	ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AL) | y, DDRP_INNOPHY_CALIB_BYPASS_AL);
	ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_AL);
	{
		union ddrp_calib al;
		al.d8 = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AL);
		serial_puts("bypass :CALIB_AL:\n");
		serial_puts("dllsel 0x"); serial_puthex(al.calib.dllsel);
		serial_puts("ophsel 0x"); serial_puthex( al.calib.ophsel);
		serial_puts("cyclesel 0x"); serial_puthex(al.calib.cyclesel);
	}
	serial_puts("DDRP_INNOPHY_CALIB_DELAY_AL 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AL));
#endif

#if 1
	m = 0 ;
	x = 0 ;
	y = 0 ;
	for(c = 0; c < 8; c ++)
		for(o = 0; o < 2; o++) {
			x = c << 4 | o << 3 | 0;
			for(d = 0; d < 8; d++) {
				ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_AH);
				ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AL) | y, DDRP_INNOPHY_CALIB_BYPASS_AL);
				/*soft_bypass_al = ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AL);
				  delay_ah = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AH);
				  serial_puts("### soft_bypass_al = 0x%x, delay_al = 0x%x\n');DBG_HEX( soft_bypass_al, delay_ah);*/
				mdelay(1);
				for(i = 0; i < 0xff; i ++) {
					val = 0;
					for(n = 0; n < 4; n++ ) {
						val |= i <<(n * 8);
						val &= 0x0000ff00;
					}
					writel(val, (addr + i * 4));
					__asm__ __volatile__("csrrw x0,0x800, %0"::"r"(addr+i*4));
					__asm__ __volatile__("csrrw x0,0x5c2, %0"::"r"(addr+i*4));
					val = val >> 8;
					if(((readl(addr + i * 4) & 0x0000ff00) >> 8)!= val)
						break;
				}
				if(i == 0xff) {
					calib_val[m].calib.cyclesel = c;
					calib_val[m].calib.ophsel = o;
					/*calib_val[m].calib.dllsel = d;*/
					m++;
					serial_puts("AH  ok VALUE 0x"); serial_puthex(y);
				}
			}
		}

	if(!m) {
		serial_puts(" AH  calib bypass fail\n");
		return ;
	}
	/* for(i = 0; i <= m; i++) */
	m /= 2;
	c = calib_val[m].calib.cyclesel;
	o = calib_val[m].calib.ophsel;
	/*d = calib_val[m].calib.dllsel;*/

	/*y = c << 4 | o << 3 | d;*/
	x = 0;
	x = c << 4 | o << 3 | 0;
	ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AL) | y, DDRP_INNOPHY_CALIB_BYPASS_AL);
	ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_AH);
	{
		union ddrp_calib ah;
		ah.d8 = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AH);
		serial_puts("bypass :CALIB_AH:\n");
		serial_puts("dllsel 0x"); serial_puthex(ah.calib.dllsel);
		serial_puts("ophsel 0x"); serial_puthex( ah.calib.ophsel);
		serial_puts("cyclesel 0x"); serial_puthex(ah.calib.cyclesel);
	}
    	serial_puts("DDRP_INNOPHY_CALIB_DELAY_AH 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AH));
#endif

#if 1
	m = 0;
	x = 0;
	y = 0;
	for(c = 0; c < 8; c ++)
		for(o = 0; o < 2; o++) {
			x = c << 4 | o << 3 | 0;
			for(d = 0; d < 8; d++) {
				y = d << 0;
				ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_BL);
				ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AH) | y, DDRP_INNOPHY_CALIB_BYPASS_AH);
				/*soft_bypass_ah = ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AH);
				  delay_bl = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BL);*/
				mdelay(1);
				for(i = 0; i < 0xff; i ++) {
					val = 0;
					for(n = 0; n < 4; n++ ) {
						val |= i <<(n * 8);
						val &=  0x00ff0000;
					}
					writel(val, (addr + i * 4));
					__asm__ __volatile__("csrrw x0,0x800, %0"::"r"(addr+i*4));
					__asm__ __volatile__("csrrw x0,0x5c2, %0"::"r"(addr+i*4));
					val = val >> 16;
					if(((readl(addr + i * 4) & 0x00ff0000 )>> 16) != val)
						break;
				}
				if(i == 0xff) {
					calib_val[m].calib.cyclesel = c;
					calib_val[m].calib.ophsel = o;
					/*calib_val[m].calib.dllsel = d;*/
					m++;
					serial_puts("BL ok VALUE 0x"); serial_puthex(x);
				}
			}
		}
	if(!m) {
		serial_puts("BL calib bypass fail\n");
		return ;
	}
	/* for(i = 0; i <= m; i++) */
	m /= 2;
	c = calib_val[m].calib.cyclesel;
	o = calib_val[m].calib.ophsel;
	/*d = calib_val[m].calib.dllsel;*/

	x = 0;
	/*x = c << 4 | o << 3 | d;*/
	x = c << 4 | o << 3 | 0;
	ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AH) | y, DDRP_INNOPHY_CALIB_BYPASS_AH);
	ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_BL);
	{
		union ddrp_calib al;
		al.d8 = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BL);
		serial_puts("bypass :CALIB_BL:\n");
		serial_puts("dllsel 0x"); serial_puthex(al.calib.dllsel);
	        serial_puts("ophsel 0x"); serial_puthex(al.calib.ophsel);
	        serial_puts("cyclesel 0x"); serial_puthex(al.calib.cyclesel);
	}

    	serial_puts("DDRP_INNOPHY_CALIB_DELAY_BL 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BL));
#endif

#if 1
	m = 0;
	x = 0;
	y = 0;
	for(c = 0; c < 8; c ++)
		for(o = 0; o < 2; o++) {
			x = c << 4 | o << 3 | 0;
			for(d = 0; d < 8; d++) {
				y = d << 4;
				ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_BH);
				ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AH) | y, DDRP_INNOPHY_CALIB_BYPASS_AH);
				/*soft_bypass_ah = ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AH);
				  delay_bh = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BH);
				*/
				mdelay(1);
				for(i = 0; i < 0xff; i ++) {
					val = 0;
					for(n = 0; n < 4; n++ ) {
						val |= i <<(n * 8);
						val &=  0xff000000;
					}
					writel(val, (addr + i * 4));
					__asm__ __volatile__("csrrw x0,0x800, %0"::"r"(addr+i*4));
					__asm__ __volatile__("csrrw x0,0x5c2, %0"::"r"(addr+i*4));
					val = val >> 24;
					if(((readl(addr + i * 4) &=  0xff000000 ) >> 24 )!= val)
						break;
				}
				if(i == 0xff) {
					calib_val[m].calib.cyclesel = c;
					calib_val[m].calib.ophsel = o;
					/*calib_val[m].calib.dllsel = d;*/
					m++;
					serial_puts("BH  ok VALUE: 0x");serial_puthex(y);
				}
			}
		}
	if(!m) {
		serial_puts(" BH calib bypass fail\n");
		return ;
	}
	/* for(i = 0; i <= m; i++) */
	m /= 2;
	c = calib_val[m].calib.cyclesel;
	o = calib_val[m].calib.ophsel;
	/*d = calib_val[m].calib.dllsel;*/

	x = 0;
	x = c << 4 | o << 3 | 0;
	/*y = c << 4 | o << 3 | d;*/
	ddr_writel(ddr_readl(DDRP_INNOPHY_CALIB_BYPASS_AH) | y, DDRP_INNOPHY_CALIB_BYPASS_AH);
	ddr_writel(x, DDRP_INNOPHY_CALIB_DELAY_BH);
	{
		union ddrp_calib ah;
		ah.d8 = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BH);
		serial_puts("bypass :CALIB_BH:\n");
		serial_puts("dllsel 0x"); serial_puthex(ah.calib.dllsel);
	        serial_puts("ophsel 0x"); serial_puthex(ah.calib.ophsel);
	        serial_puts("cyclesel 0x"); serial_puthex(ah.calib.cyclesel);
	}
    	serial_puts("DDRP_INNOPHY_CALIB_DELAY_BH 0x"); serial_puthex(ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BH));
#endif


}
static void ddrp_calibration(int bypass)
{
	if(bypass)
		ddrp_software_calibration();
	else
		ddrp_hardware_calibration();
}

static void ddr_phy_init(void)
{
	FUNC_ENTER();
	ddrp_pll_init();
	/*ddrp_register_cfg();*/
	FUNC_EXIT();
}

static void ddrc_dfi_init(enum ddr_type type, int bypass)
{
	FUNC_ENTER();
	ddr_writel(DDRC_DWCFG_DFI_INIT_START, DDRC_DWCFG); // dfi_init_start high
	ddr_writel(0, DDRC_DWCFG); // set buswidth 16bit
	while(!(ddr_readl(DDRC_DWSTATUS) & DDRC_DWSTATUS_DFI_INIT_COMP)) //polling dfi_init_complete
		asm("nop");

	ddr_writel(0, DDRC_CTRL); //set dfi_reset_n high
	ddr_writel(DDRC_CFG_VALUE, DDRC_CFG);
	ddr_writel(DDRC_CTRL_CKE, DDRC_CTRL); // set CKE to high

	switch(type) {
	case LPDDR2:
#define DDRC_LMR_MR(n)                                                  \
        1 << 1| DDRC_DLMR_VALUE | DDRC_LMR_START | DDRC_LMR_CMD_LMR |   \
		((DDR_MR##n##_VALUE & 0xff) << 24) |			\
		(((DDR_MR##n##_VALUE >> 8) & 0xff) << (16))
		ddr_writel(DDRC_LMR_MR(63), DDRC_LMR); //set MRS reset
		ddr_writel(DDRC_LMR_MR(10), DDRC_LMR); //set IO calibration
		ddr_writel(DDRC_LMR_MR(1), DDRC_LMR); //set MR1
		ddr_writel(DDRC_LMR_MR(2), DDRC_LMR); //set MR2
		ddr_writel(DDRC_LMR_MR(3), DDRC_LMR); //set MR3
#undef DDRC_LMR_MR
		break;
	case DDR3:
#define DDRC_LMR_MR(n)							\
        DDRC_DLMR_VALUE | DDRC_LMR_START | DDRC_LMR_CMD_LMR |		\
		((DDR_MR##n##_VALUE & 0xffff) << DDRC_LMR_DDR_ADDR_BIT) | \
		(((DDR_MR##n##_VALUE >> 16) & 0x7) << DDRC_LMR_BA_BIT)

		ddr_writel(DDRC_LMR_MR(0), DDRC_LMR); //MR0
		ddr_writel(DDRC_LMR_MR(1), DDRC_LMR); //MR1
		ddr_writel(DDRC_LMR_MR(2), DDRC_LMR); //MR2
		ddr_writel(DDRC_LMR_MR(3), DDRC_LMR); //MR3
		ddr_writel(DDRC_DLMR_VALUE | DDRC_LMR_START | DDRC_LMR_CMD_ZQCL_CS0, DDRC_LMR); //ZQCL
#undef DDRC_LMR_MR
		break;
	default:
		ddr_hang();
	}

	FUNC_EXIT();
}

struct ddr_calib_value {
	unsigned int rate;
	unsigned int refcnt;
	unsigned char bypass_al;
	unsigned char bypass_ah;
};

#if 0
static void ddr_calibration(struct ddr_calib_value *dcv, int div)
{
	unsigned int val;

	// Set change_en
	val = readl(CPM_DDRCDR);
	val |= ((1 << 29) | (1 << 25));
	writel(val, CPM_DDRCDR) = val;
	while((readl(CPM_DDRCDR) & (1 << 24)));
	/* // Set clock divider */
	val = readl(CPM_DDRCDR);
	val &= ~(0xf);
	val |= div;
	writel(val, CPM_DDRCDR);

	// Polling PHY_FREQ_DONE
	while(((ddr_readl(DDRC_DWSTATUS) & (1 << 3 | 1 << 1)) & 0xf) != 0xa);
	ddrp_hardware_calibration();
	/* ddrp_software_calibration(); */

	dcv->bypass_al = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AL);
    	/* serial_puts("auto :CALIB_AL: dcv->bypss_al "); serial_puthex(dcv->bypass_al); */
	dcv->bypass_ah = ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AH);
    	/* serial_puts("auto:CAHIB_AH: dcv->bypss_ah "); serial_puthex(dcv->bypass_ah); */

	// Set Controller Freq Exit
	val = ddr_readl(DDRC_DWCFG);
	val |= (1 << 2);
	ddr_writel(val, DDRC_DWCFG);

	// Clear Controller Freq Exit
	val = ddr_readl(DDRC_DWCFG);
	val &= ~(1 << 2);
	ddr_writel(val, DDRC_DWCFG);

	val = readl(CPM_DDRCDR);
	val &= ~((1 << 29) | (1 << 25));
	writel(val, CPM_DDRCDR);
}
#endif

void inline set_ddr_access(unsigned int d)
{
	unsigned int val;

	val = readl(0x12200fe0);
	val &= ~(1 << 20);
	val |= ((d & 1) << 20);
	writel(val, 0x12200fe0);
}

/* DDRC init */
void ddr3_init(void)
{
	enum ddr_type type;
	int bypass = 0;

	serial_puts("sdram init start\n");

	set_ddr_access(1);

	type = get_ddr_type();

	ddrc_reset_phy();

	ddr_phy_init();

	ddrc_dfi_init(type, bypass);

	ddrc_prev_init();

	serial_puts("@DDRP_INNOPHY_CALIB_DELAY_AL 0x"); serial_puthex( ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AL));
	serial_puts("@DDRP_INNOPHY_CALIB_DELAY_AH 0x"); serial_puthex( ddr_readl(DDRP_INNOPHY_CALIB_DELAY_AH));
	serial_puts("@DDRP_INNOPHY_CALIB_DELAY_BL 0x"); serial_puthex( ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BL));
	serial_puts("@DDRP_INNOPHY_CALIB_DELAY_BH 0x"); serial_puthex( ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BH));

	/**
	 * bypass  = 1 or calibration = 0
	 */
	bypass = 1;
	ddrp_calibration(bypass);

	/*ddr_writel(0x3c, DDRP_INNOPHY_CALIB_DELAY_BL);*/
	/*ddr_writel(0x3c, DDRP_INNOPHY_CALIB_DELAY_BH);*/
	/*serial_puts("DDRP_INNOPHY_CALIB_DELAY_BL 0x"); serial_puthex( ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BL));*/
	/*serial_puts("DDRP_INNOPHY_CALIB_DELAY_BH 0x"); serial_puthex( ddr_readl(DDRP_INNOPHY_CALIB_DELAY_BH));*/

	dump_ddrp_register();
	/*writel(0x130113D4) = 0x11;*/
	/*writel(0x130113D8) = 0x11;*/
	serial_puts("DDRP_INNOPHY_DOS 0X3D4 0x"); serial_puthex(readl(0x130113D4));
	serial_puts("DDRP_INNOPHY_DOS 0X3D8 0x"); serial_puthex(readl(0x130113D8));

	ddrc_post_init();

	/* if(ddr_hook && ddr_hook->post_ddr_init) */
		/* ddr_hook->post_ddr_init(type); */

	if(DDRC_AUTOSR_EN_VALUE)
		/* ddr_writel(DDRC_AUTOSR_CNT_VALUE, DDRC_AUTOSR_CNT); */
		ddr_writel(1, DDRC_AUTOSR_EN);
	else
		ddr_writel(0, DDRC_AUTOSR_EN);

	ddr_writel(DDRC_HREGPRO_VALUE, DDRC_HREGPRO);
	ddr_writel(DDRC_PREGPRO_VALUE, DDRC_PREGPRO);

	dump_ddrc_register();

	/* DDRC address remap configure*/
	serial_puts("ddrc init finished\n");
}

void ddr4_init(void)
{
	set_ddr_access(1);
}

void ddr_init(int type)
{
	if(type)
		ddr4_init();
	else
		ddr3_init();
}
